
//
// 110115 - AH - Created.
// 110116a- AH - rinse(Network).
// 110116b- AH - retrieve(NetworkScan, List<AttributeObject>, RetrievalEvent)
//               declared, todo implement.
// 110116c- AH - (Bien) Did todo of 110116b.
// 110116d- AH - (Bien) Removes "key" attributes from nodes in rinse.
// 110117 - AH - (Hector) Integrates new NetworkScan method 
//               getMaxLinkageBetweenClusters.
//

package org.soekrat.engine.retrieval.network;

import static org.soekrat.engine.retrieval.network.NetworkScan.getMaxLinkageBetweenClusters;
import static org.soekrat.engine.retrieval.network.NetworkScan.getMaxLinks;
import static org.soekrat.engine.util.Util.getRandom;

import java.util.List;

import org.soekrat.AttributeObject;
import org.soekrat.engine.network.Cluster;
import org.soekrat.engine.network.Network;
import org.soekrat.engine.network.NetworkNode;
import org.soekrat.engine.wheel.retrieval.RetrievalEvent;
import org.soekrat.engine.wheel.retrieval.RetrievalEventSource;
import org.soekrat.engine.wheel.retrieval.RetrievalEventType;

/**
 * <p>
 *   {@link Network} retrieval strategy implementation. 
 * </p>
 * @author Laetitia Le Chatton (LLC) 
 * @author Arne Halvorsen (AH) 
 */
public final class NetworkRetrievalStrategy 
{ 
  private final static RetrievalEventType c_pass=RetrievalEventType.PASS;
  
  private final static RetrievalEventType c_step=RetrievalEventType.STEP;
  
  private final static RetrievalEventType c_paint=RetrievalEventType.PAINT;
  
  private final static RetrievalEventSource c_source=
    RetrievalEventSource.NETWORK;

  // Never instantiated, static algorithm methods only.
  private NetworkRetrievalStrategy(){}
  
  /**
   * <p>
   *   Performs retrieval. 
   * </p>
   * @param scan  Result of matching keywords with resources in the
   *              {@link Network}.
   * @param paint Where retrieved resources are put.
   * @param prev  The previous  
   *              {@link RetrievalEvent}.
   */
  public static void retrieve(NetworkScan scan, List<AttributeObject> paint, 
    RetrievalEvent prev)
  {
    if (scan.noMatch())
    {
      // Pass.
      prev=new RetrievalEvent(prev, "Network: Pass", c_pass, c_source);
    }
    else if (scan.singleKeyword())
    {
      RetrievalEvent curr=new RetrievalEvent(prev, "Network: Single keyword", 
        c_step, c_source);
     
      Cluster cluster=scan.getCluster();
      
      paint.add(cluster);
      
      curr=new RetrievalEvent(curr, "Retrieved : "+
        matchedClusterNodeNamesToString(cluster), c_paint, c_source);
      curr.setAttribute("cluster", cluster);
    }
    else if (scan.multiKeyword())
    {
      RetrievalEvent curr=new RetrievalEvent(prev, "Network: Multi keyword", 
        c_step, c_source);
      
      List<Cluster> richest=scan.getRichestCluster();
      if (richest.size()>1)
      {
        curr=new RetrievalEvent(prev, "Network: More than one richest", 
          c_step, c_source);
        
        List<Cluster> mostLinkage=getMaxLinkageBetweenClusters(richest);
        if (mostLinkage.size()>1)          
        {
          curr=new RetrievalEvent(prev, 
            "Network: More than one with most linkage between clusters", 
            c_step, c_source);
          
          List<Cluster> maxLinks=getMaxLinks(mostLinkage);  
        
          //List<Cluster> max=scan.getMaxLinkage(); // Should apply 2 list!
          if (maxLinks.size()>1)
          {
            curr=new RetrievalEvent(prev, 
              "Network: More than one with max linkage", c_step, c_source);
          
            Cluster cluster=getRandom(maxLinks);
          
            paint.add(cluster);
          
            curr=new RetrievalEvent(curr, "Retrieved : "+
              matchedClusterNodeNamesToString(cluster), c_paint, c_source);
            curr.setAttribute("cluster", cluster);
          }
          else
          {
            curr=new RetrievalEvent(curr, "Network: Only one with max linkage", 
              c_step, c_source);
          
            Cluster cluster=maxLinks.get(0);

            paint.add(cluster);
          
            curr=new RetrievalEvent(curr, "Retrieved : "+
              matchedClusterNodeNamesToString(cluster), c_paint, c_source);
            curr.setAttribute("cluster", cluster);
          }
        }
        else
        {
          curr=new RetrievalEvent(curr, 
            "Network: Only one with most linkage between clusters", c_step, 
            c_source);
          
          Cluster cluster=mostLinkage.get(0);
          
          paint.add(cluster);
          
          curr=new RetrievalEvent(curr, "Retrieved : "+
            matchedClusterNodeNamesToString(cluster), c_paint, c_source);
          curr.setAttribute("cluster", cluster);
        }
      }
      else
      {
        curr=new RetrievalEvent(prev, "Network: One richest", c_step, c_source);
        
        Cluster cluster=richest.get(0);
        
        paint.add(cluster);
        
        curr=new RetrievalEvent(curr, "Retrieved : "+
          matchedClusterNodeNamesToString(cluster), c_paint, c_source);
        curr.setAttribute("cluster", cluster);
      }
    }
  }
  
  /**
   * <p>
   *   To be invoked before retrieval to "rinse" network for "dirt" from
   *   previous retrieval.
   * </p>
   * @param forest {@link Network} to rinse.
   */
  public static void rinse(Network network)
  {
    if (network==null)
    {
      throw new NullPointerException("network");
    }
    
    for (Cluster curr : network.getClusters())
    {
      curr.removeAttribute("linkage");
      for (NetworkNode node : curr) 
      {
        node.removeAttribute("key");  
      }
    }
  }
  
  public static String matchedClusterNodeNamesToString(Cluster cluster)
  {
    StringBuilder sb=new StringBuilder();
    boolean first=true;
    for (NetworkNode node : cluster) 
    {
      if (!first) sb.append(", ");
      first=false;
      sb.append(node.getName());   
    }
    return sb.toString();
  }
  
}
